#include "DVRCommonPCH.h"
#include "TransferFunction.h"
#include "DVRUtils.h"

using namespace hiveDVR;

//**************************************************************************************************
//FUNCTION:
//TODO: load from file
void CTransferFunction::init(size_t vExtent)
{
	m_Extent = vExtent;
	m_ColorSet.resize(m_Extent * m_Extent);
}

//**************************************************************************************************
//FUNCTION:
void CTransferFunction::update(int vMinIntensity, int vMaxIntensity, int vMinGradient, int vMaxGradient)
{
	reset();
	m_UseGradient = (vMaxGradient != vMinGradient);
	float IntensityInterval = 1.0f / static_cast<float>(vMaxIntensity - vMinIntensity);
	float GradientInterval = m_UseGradient ? 1.0f / static_cast<float>(vMaxGradient - vMinGradient) : 0.0f;

	size_t Index = 0;
	for (int g = 0; g < m_Extent; ++g)
	{
		for (int i = 0; i < m_Extent; ++i, ++Index)
		{
			float ClampedIntensity = clamp(static_cast<float>(i - vMinIntensity) * IntensityInterval, 0.0f, 1.0f);
			float ClampedGradient = m_UseGradient ? clamp(static_cast<float>(g - vMinGradient) * GradientInterval, 0.0f, 1.0f) : 1.0f;
			float Alpha = ClampedIntensity * ClampedGradient;
			m_ColorSet[Index] = glm::vec4(Alpha, Alpha, Alpha, Alpha);
		}
	}
}

//**************************************************************************************************
//FUNCTION: does not use gradient
void CTransferFunction::addPoint(int vValue, float vOpacity)
{
	_ASSERTE(vValue < m_Extent);
	glm::vec4 Color(vOpacity, vOpacity, vOpacity, vOpacity);
	addPoint(vValue, Color);
}

//**************************************************************************************************
//FUNCTION: does not use gradient
void CTransferFunction::addPoint(int vValue, const glm::vec4& vColor)
{
	_ASSERTE(vValue < m_Extent);
	std::vector<STransferFunctionPoint>::iterator it = std::upper_bound(m_PointSet.begin(), m_PointSet.end(), static_cast<STransferFunctionPoint>(vValue));

	it = m_PointSet.emplace(it, vValue, vColor);
	m_ColorSet[vValue] = vColor;
	__updatePalette(it);
}

//**************************************************************************************************
//FUNCTION:
void CTransferFunction::reset()
{
	m_PointSet.clear();
	memset(m_ColorSet.data(), 0, m_ColorSet.size() * sizeof(decltype(m_ColorSet)::value_type));
}

//**************************************************************************************************
//FUNCTION: does not use gradient
void CTransferFunction::__updatePalette(std::vector<STransferFunctionPoint>::iterator& vIterator)
{
	if (m_PointSet.empty()) return;

	STransferFunctionPoint NextPoint = (vIterator == (m_PointSet.end() - 1) ? *vIterator : *(vIterator + 1));
	STransferFunctionPoint PrevousPoint = (vIterator == m_PointSet.begin() ? *vIterator : *(vIterator - 1));
	STransferFunctionPoint CurrentPoint = *vIterator;

	__linearInterpolateBetween(PrevousPoint, CurrentPoint);
	__linearInterpolateBetween(CurrentPoint, NextPoint);
}

//**************************************************************************************************
//FUNCTION: does not use gradient
void CTransferFunction::__linearInterpolateBetween(const STransferFunctionPoint& vLeft, const STransferFunctionPoint& vRight)
{
	if (vLeft.Scalar >= vRight.Scalar) return;

	float Step = 1.0f / (vRight.Scalar - vLeft.Scalar);
	for (int i = vLeft.Scalar + 1; i < vRight.Scalar; ++i)
	{
		m_ColorSet[i] = m_ColorSet[i - 1] + Step * (vRight.Color - vLeft.Color);
	}
}